package org.saxonextensions.jcr;

import net.sf.saxon.Configuration;
import net.sf.saxon.om.DocumentInfo;
import net.sf.saxon.om.NamePool;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.type.Type;

import javax.jcr.Node;
import javax.jcr.Session;
import javax.jcr.RepositoryException;
import javax.jcr.Property;
import java.util.Iterator;
import java.util.Collections;

/**
 * Wrap JCR Node into Saxon DocumentInfo
 *
 * @author Ivan Latysh
 * @since 8-Oct-2008 9:16:20 PM
 */
public class JCRDocumentWrapper extends JCRNodeWrapper implements DocumentInfo {
  /** Current configuration */
  protected Configuration config;
  /** Base URI */
  protected String baseURI;
  /** Uniques document number */
  protected int documentNumber;
  /** JCR namespaces */
  protected int[] jcrNamespaceList;

  /**
   * Create a Saxon wrapper for JCR Repository
   *
   * @param root JCR root node
   * @param baseURI The base URI for all the nodes in the document
   * @param config Configuration
   */
  public JCRDocumentWrapper(Node root, String baseURI, Configuration config) {
    super(root, null, 0);
    this.node = root;
    this.nodeKind = Type.DOCUMENT;
    this.baseURI = baseURI;
    this.docWrapper = this;
    setConfiguration(config);
  }

  /**
   * Wrap an JCR node in the NodeInfo wrapper
   *
   * @param node The node to be wrapped.
   * @return wrapped node
   */
  public NodeInfo wrap(Object node) {    
    return (node == this.node) ?this :makeWrapper(node, this);
  }

  /**
   * Return the unique document number
   *
   * @return document number
   */
  public int getDocumentNumber() {
    return documentNumber;
  }

  /**
   * Return declared namespaces
   *
   * @param buffer buffer
   * @return JCR Namespace list
   */
  public int[] getDeclaredNamespaces(int[] buffer) {
    return jcrNamespaceList;
  }

  /**
   * Get the element with a given UUID
   *
   * @param id JCR UUID
   * @return element with given UUID or <tt>null</tt> if UUID is not valid or node is not found
   */
  public NodeInfo selectID(String id) {
    try {
      Session session = ((Node)node).getSession();
      return wrap(session.getNodeByUUID(id));
    } catch (RepositoryException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }    
  }

  /**
   * Return iterator with unparsed entity names
   *
   * @return empty list iterator
   */
  public Iterator getUnparsedEntityNames() {
    return Collections.emptyList().iterator();
  }

  /**
   * Get the unparsed entity with a given name
   *
   * @param name the name of the entity
   * @return null
   */
  public String[] getUnparsedEntity(String name) {
    return null;
  }

  /**
   * Return current configuration
   *
   * @return configuration
   */
  public Configuration getConfiguration() {
    return config;
  }

  /**
   * Set the configuration
   *
   * @param config configuration to set
   */
  public void setConfiguration(Configuration config) {
    this.config = config;
    if (null!=config) {
      documentNumber = config.getDocumentNumberAllocator().allocateDocumentNumber();
      // get name pool
      NamePool namepool = config.getNamePool();
      // proceed if name pool is not null
      if (null!=namepool) {
        // if node it not null, get registered namespace prefixed
        if (null!=node) {
          try {
            // frankly speaking, node always should be a node, but just to be on the safe side, check for property as well
            Node _node = null;
            if (node instanceof Node) {
              _node = (Node) node;
            } else if (node instanceof Property) {
              _node = ((Property) node).getNode();
            }
            // if JCRNode has been found
            if (null!=_node) {
              Session session = _node.getSession();
              // get registered namespace prefixes.
              String[] prefixes = session.getNamespacePrefixes();
              jcrNamespaceList = new int[prefixes.length];
              // register all prefixes
              int i=0;
              for (String prefix: prefixes) {
                String uri = session.getNamespaceURI(prefix);
                jcrNamespaceList[i]=namepool.allocateNamespaceCode(prefix, uri);
                namepool.allocateCodeForURI(uri);
                i++;
              }
            }
          } catch (RepositoryException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
          }
        }
        // Register some basic prefixed
        else {

          jcrNamespaceList = new int[4];

          jcrNamespaceList[0]=namepool.allocateNamespaceCode("jcr", "http://www.jcp.org/jcr/1.0");
          namepool.allocateCodeForURI("http://www.jcp.org/jcr/1.0");

          jcrNamespaceList[1]=namepool.allocateNamespaceCode("nt", "http://www.jcp.org/jcr/nt/1.0");
          namepool.allocateCodeForURI("http://www.jcp.org/jcr/nt/1.0");

          jcrNamespaceList[2]=namepool.allocateNamespaceCode("mix", "http://www.jcp.org/jcr/mix/1.0");
          namepool.allocateCodeForURI("http://www.jcp.org/jcr/mix/1.0");

          jcrNamespaceList[3]=namepool.allocateNamespaceCode("sv", "http://www.jcp.org/jcr/sv/1.0");
          namepool.allocateCodeForURI("http://www.jcp.org/jcr/sv/1.0");
        
        }
      } else {
        jcrNamespaceList = new int[0];
      }
    }
  }

  /**
   * Get the name pool used for the names in this document
   *
   * @return name pool, or <tt>null</tt> if configuration is <tt>null</tt>
   */
  public NamePool getNamePool() {
    return null!=config ?config.getNamePool() :null;
  }

}